package cn.gaaidou.rpc.framework;

import cn.gaaidou.common.utils.ClazzUtil;
import cn.gaaidou.common.utils.JdkSerializationUtil;
import cn.gaaidou.rpc.framework.vo.RpcClassMapperVo;
import cn.gaaidou.rpc.framework.vo.RpcReqVo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author Martin
 * @date 2021/7/5 17:24
 */
public class RpcInvoker {

    private static final String SERVICE_IMPL_PACKAGE = "cn.gaaidou.rpc.server.service.impl";

    public static byte[] invoke(byte[] reqData) {
        // 1、反序列化请求参数
        RpcReqVo rpcReqVo = (RpcReqVo) JdkSerializationUtil.deserialize(reqData);

        try {
            // 2、寻找实现类、反序列化请求方法、请求参数值
            Class<?> serviceClass = getImplClazz(Class.forName(rpcReqVo.getClazzName()));
            List<RpcClassMapperVo> reqInfos = rpcReqVo.getReqInfos();
            // 方法类型(用于寻找方法, Java支持方法Override, 因此需要方法名称 + 方法参数类型 两个维度确定)
            Class<?>[] paramsClazz = reqInfos.stream().map(RpcClassMapperVo::getClazz).toArray(t -> (new Class<?>[1]));
            // 方法参数
            Object[] args = reqInfos.stream().map(v -> JdkSerializationUtil.deserialize(v.getData())).toArray(t -> new Object[1]);
            // 寻找对应的实现方法
            Method method = serviceClass.getMethod(rpcReqVo.getMethodName(), paramsClazz);
            // 反射调用实现方法, 并返回结果
            Optional<Constructor<?>> optionalConstructor = Arrays.stream(serviceClass.getConstructors()).filter(v -> v.getParameterCount() == 0).findFirst();
            if (!optionalConstructor.isPresent()) {
                throw new RuntimeException("服务提供者必须存在无参构造方法");
            }
            Object returnResult = method.invoke(optionalConstructor.get().newInstance(), args);


            // 4、序列化结果
            return JdkSerializationUtil.serialize(returnResult);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("找不到服务提供者所在类");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("找不到服务提供者所在方法");
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            throw new RuntimeException("执行构造方法出现异常");
        }
    }

    private static Class<?> getImplClazz(Class<?> interfaceClazz) {
        return ClazzUtil.getClazzByPackage(SERVICE_IMPL_PACKAGE, interfaceClazz).iterator().next();
    }
}
